package ru.demo.abstarctservice.app.impl;

import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;
import ru.demo.abstarctservice.adapter.exceptions.EntityNotFoundException;
import ru.demo.abstarctservice.adapter.jpa.AbstractRepository;
import ru.demo.abstarctservice.adapter.mapper.AbstractMapper;
import ru.demo.abstarctservice.adapter.rabbit.RabbitMQEvent;
import ru.demo.abstarctservice.adapter.rabbit.RabbitMQOperation;
import ru.demo.abstarctservice.app.api.AbstractService;
import ru.demo.abstarctservice.entity.BaseEntity;

import java.util.List;
import java.util.UUID;

@RequiredArgsConstructor
public class AbstractServiceImpl<Entity extends BaseEntity, Dto> implements AbstractService<Entity> {

    protected final AbstractRepository<Entity> repository;
    protected final AbstractMapper<Entity, Dto> mapper;
    protected final ApplicationEventPublisher applicationEventPublisher;

    @Override
    @Transactional(readOnly = true)
    public List<Entity> getAll() {
        return repository.findAll();
    }

    @Override
    @Transactional(readOnly = true)
    public Entity getById(UUID id) {
        return repository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException(id));
    }

    @Override
    @Transactional
    public Entity create(Entity entity) {
        if (entity.getId() == null) {
            entity.setId(UUID.randomUUID());
        }
        Entity savedEntity = repository.save(entity);
        publishRabbitMQEvent(savedEntity, RabbitMQOperation.CREATE);
        return savedEntity;
    }

    @Override
    @Transactional
    public Entity update(UUID id, Entity entity) {
        Entity existingEntity = repository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException(id, entity.getClass()));
        mapper.updateEntity(entity, existingEntity);
        publishRabbitMQEvent(existingEntity, RabbitMQOperation.UPDATE);
        return existingEntity;
    }

    @Override
    @Transactional
    public void delete(UUID id) {
        Entity existingEntity = repository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException(id));
        repository.delete(existingEntity);
    }

    protected void publishRabbitMQEvent(Entity entity, RabbitMQOperation operation) {
        applicationEventPublisher.publishEvent(
                new RabbitMQEvent<>(mapper.mapToDto(entity), entity.getClass().getSimpleName(), operation));
    }

}
